#include "hw_rpu.h"

void rpu_start_work(void __iomem *regbase)
{
	writel((1<<RPU_WORK_START_BIT), RPU_WORK_REG(regbase));
}

void rpu_stop_work(void __iomem *regbase)
{
	writel((0<<RPU_WORK_START_BIT), RPU_WORK_REG(regbase));
}

void rpu_start_irq(void __iomem *regbase)
{
	writel((0<<RPU_INT_BIT), RPU_INT_MASK(regbase));
}

void rpu_stop_irq(void __iomem *regbase)
{
	writel((1<<RPU_INT_BIT), RPU_INT_MASK(regbase));
}

void rpu_clr_irq(void __iomem *regbase)
{
	writel(0x0, RPU_OVER(regbase));
}

uint32_t rpu_get_int_status(void __iomem *regbase)
{
	uint32_t status = 0x0;

	status = readl(RPU_INT_STATE(regbase)) & (1<<RPU_INT_BIT);
	return status;
}

uint32_t rpu_get_status(void __iomem *regbase)
{

	uint32_t status = 0x0;

	status = (readl(RPU_WORK_REG(regbase)))&(1<<RPU_WORK_BUSY_BIT);
	return status;
}

uint32_t rpu_get_status_cache(void __iomem *regbase)
{

	uint32_t status = 0x0;

	status = (readl(RPU_OVER(regbase)))&(0X03);
	return status;
}

int rpu_wait_free(void __iomem *regbase, uint32_t loop)
{
	uint32_t status;

	status = 0x00;
	while ((status != 0x3) && (loop > 0)) {
		status = rpu_get_status_cache(regbase);
		//delay something
		//delay_us(50);
		loop--;
	}

	if (loop != 0) {
		rpu_clr_irq(regbase);
		return 0;
	}

	return -1;
}

void para_cfg(void __iomem *regbase, uint32_t *para_ptr, int para_size)
{
	int i;
	void __iomem *para_waddr;
	void __iomem *rpu_base = regbase - RPU_REG_OFFSET;

	for (i = 0; i < para_size; i++) {
		para_waddr = rpu_base + *(para_ptr+i*2+0);
		writel(*(para_ptr+i*2+1), para_waddr);
	}
}

void para_cfg_sbox(
		void __iomem *regbase,
		uint32_t *para_ptr,
		int para_size,
		int base_offset)
{
	int i;
	void __iomem *para_waddr;
	void __iomem *rpu_base = regbase - RPU_REG_OFFSET;

	for (i = 0; i < para_size; i++) {
		para_waddr = rpu_base + *(para_ptr+i*2+0) + base_offset;
		writel(*(para_ptr+i*2+1), para_waddr);
	}
}

void para_read_contiue_bybyte(
		void __iomem *regbase,
		uint8_t *para_ptr,
		int para_size,
		int base_offset_start)
{
	int i;
	void __iomem *para_waddr;
	void __iomem *rpu_base = regbase - RPU_REG_OFFSET;

	for (i = 0; i < para_size; i++) {
		para_waddr = rpu_base + base_offset_start + i;
		*(para_ptr+i) = readb(para_waddr);
	}
}

void para_write_contiue_bybyte(
		void __iomem *regbase,
		uint8_t *para_ptr,
		int para_size,
		int base_offset_start)
{
	int i;
	void __iomem *para_waddr;
	void __iomem *rpu_base = regbase - RPU_REG_OFFSET;

	for (i = 0; i < para_size; i++) {
		para_waddr = rpu_base + base_offset_start + i;
		writeb(*(para_ptr+i), para_waddr);
	}
}


int rpu_key_setup_wait(
		void __iomem		*regbase,
		const uint8_t		key[],
		uint32_t			key_len,
		uint32_t			loop)
{
	uint32_t mode_value = 0x0;
	uint32_t temp[6];
	uint32_t i;

	if (key == NULL)
		return -1;

	if (key_len != 16
		&& key_len != 8
		&& key_len != 24)
		return -2;

	for (i = 0; i < key_len/4; i++)
		temp[i] =
		key[i*4+0]<<24
		| key[i*4+1]<<16
		| key[i*4+2]<<8
		| key[i*4+3];

	if (key_len == 16) {
		rpu_stop_work(regbase);

		writel(temp[0], RPU_KEY0_REG(regbase));
		writel(temp[1], RPU_KEY1_REG(regbase));
		writel(temp[2], RPU_KEY2_REG(regbase));
		writel(temp[3], RPU_KEY3_REG(regbase));
		writel(0x0, RPU_PLAIN_SIZE(regbase));

		mode_value = readl(RPU_ENCRYPT_MODE(regbase));
		setbit(mode_value, RPU_ENCRYPT_KEY_EXPAND_BIT);
		writel(mode_value, RPU_ENCRYPT_MODE(regbase));
	} else if (key_len == 8) {
		rpu_stop_work(regbase);

		writel(temp[0], RPU_KEY0_REG(regbase));
		writel(temp[1], RPU_KEY1_REG(regbase));
		writel(0x0, RPU_PLAIN_SIZE(regbase));

		mode_value = readl(RPU_ENCRYPT_MODE(regbase));
		setbit(mode_value, RPU_ENCRYPT_KEY_EXPAND_BIT);
		writel(mode_value, RPU_ENCRYPT_MODE(regbase));
	} else if (key_len == 24) {
		rpu_stop_work(regbase);

		writel(temp[0], RPU_KEY0_REG(regbase));
		writel(temp[1], RPU_KEY1_REG(regbase));
		writel(temp[2], RPU_KEY2_REG(regbase));
		writel(temp[3], RPU_KEY3_REG(regbase));
		writel(temp[4], RPU_KEY4_REG(regbase));
		writel(temp[5], RPU_KEY5_REG(regbase));
		writel(0x0, RPU_PLAIN_SIZE(regbase));

		mode_value = readl(RPU_ENCRYPT_MODE(regbase));
		setbit(mode_value, RPU_ENCRYPT_KEY_EXPAND_BIT);
		writel(mode_value, RPU_ENCRYPT_MODE(regbase));
	} else {
		return -2;
	}

	rpu_stop_irq(regbase);
	rpu_start_work(regbase);

	while ((rpu_get_status_cache(regbase) != 0x3) && (loop > 0))
		loop--;

	if (loop != 0)
		rpu_clr_irq(regbase);

	rpu_stop_work(regbase);

	return loop ? 0 : -3;
}

int rpu_iv_setup_imm(
	void __iomem		*regbase,
	uint8_t				iv[],
	uint32_t			iv_len)
{
	uint32_t temp[4];
	uint32_t i;

	if (iv == NULL)
		return -1;

	if (iv_len != 16
		&& iv_len != 8)
		return -2;

	for (i = 0; i < iv_len/4; i++)
		temp[i] =
		iv[i*4+0]<<24
		| iv[i*4+1]<<16
		| iv[i*4+2]<<8
		| iv[i*4+3];

	if (iv_len == 16) {
		writel(temp[0], RPU_R0_REG(regbase));
		writel(temp[1], RPU_R1_REG(regbase));
		writel(temp[2], RPU_R2_REG(regbase));
		writel(temp[3], RPU_R3_REG(regbase));

		return 0;
	} else if (iv_len == 8) {
		writel(temp[0], RPU_R0_REG(regbase));
		writel(temp[1], RPU_R1_REG(regbase));

		return 0;
	} else {
		return -2;
	}
}

int rpu_iv_get_imm(
	void __iomem		*regbase,
	uint8_t				iv[],
	uint32_t			iv_len)
{
	uint32_t temp[4];
	uint32_t i;

	if (iv == NULL)
		return -1;

	if (iv_len == 16) {
		temp[0] = readl(RPU_R0_REG(regbase));
		temp[1] = readl(RPU_R1_REG(regbase));
		temp[2] = readl(RPU_R2_REG(regbase));
		temp[3] = readl(RPU_R3_REG(regbase));

		return 0;
	} else if (iv_len == 8) {
		temp[0] = readl(RPU_R0_REG(regbase));
		temp[1] = readl(RPU_R1_REG(regbase));

		return 0;
	} else {
		return -2;
	}

	for (i = 0; i < iv_len/4; i++) {
		iv[i*4+0] = (uint8_t)(temp[i]>>24);
		iv[i*4+1] = (uint8_t)(temp[i]>>16);
		iv[i*4+2] = (uint8_t)(temp[i]>>8);
		iv[i*4+3] = (uint8_t)(temp[i]);
	}
}

